/*
 * UAE - The Un*x Amiga Emulator
 *
 * Support for Linux/USS sound
 *
 * Copyright 1997 Bernd Schmidt
 */

#pragma once

#define SOUNDSTUFF 1

extern ushort paula_sndbuffer[];
extern ushort* paula_sndbufpt;
extern int paula_sndbufsize;
extern void finish_sound_buffer();
// extern void restart_sound_buffer();
extern int init_sound();
extern void close_sound();
extern int setup_sound();
extern void resume_sound();
extern void pause_sound();
// extern void reset_sound();
extern void sound_setadjust(double);
extern void enumerate_sound_devices();
extern int drivesound_init();
extern void drivesound_free();
extern void sound_mute(int);
extern void sound_volume(int);
extern void set_volume(int, int);
extern void master_sound_volume(int);

struct sound_dp;

struct sound_data
{
    int waiting_for_buffer;
    // int devicetype;
    int obtainedfreq;
    int paused;
    int mute;
    int channels;
    int freq;
    int samplesize;
    int sndbufsize;
    struct sound_dp* data;
};

void send_sound(struct sound_data* sd, ushort* sndbuffer);
int open_sound_device(struct sound_data* sd, int bufsize, int freq, int channels);
void close_sound_device(struct sound_data* sd);
void pause_sound_device(struct sound_data* sd);
void resume_sound_device(struct sound_data* sd);
void set_volume_sound_device(struct sound_data* sd, int volume, int mute);
// int get_offset_sound_device(struct sound_data* sd);
// int blocking_sound_device(struct sound_data* sd);

#if SOUNDSTUFF > 0
extern int outputsample, doublesample;
#endif

static ushort* paula_sndbufpt_prev, * paula_sndbufpt_start;

static __forceinline void set_sound_buffers()
{
    #if SOUNDSTUFF > 1
    paula_sndbufpt_prev = paula_sndbufpt_start;
    paula_sndbufpt_start = paula_sndbufpt;
    #endif
}

static __forceinline void check_sound_buffers()
{
    #if SOUNDSTUFF > 1
    int len;
    #endif

    // if (g_curr_conf.sound_stereo == SND_4CH_CLONEDSTEREO)
    // {
    //     ((ushort*)paula_sndbufpt)[0] = ((ushort*)paula_sndbufpt)[-2];
    //     ((ushort*)paula_sndbufpt)[1] = ((ushort*)paula_sndbufpt)[-1];
    //     paula_sndbufpt = (ushort*)(((byte*)paula_sndbufpt) + 2 * 2);
    // }
    // else if (g_curr_conf.sound_stereo == SND_6CH_CLONEDSTEREO)
    // {
    //     short* p = ((short*)paula_sndbufpt);
    //     int sum;
    //     p[2] = p[-2];
    //     p[3] = p[-1];
    //     sum = (int)(p[-2]) + (int)(p[-1]) + (int)(p[2]) + (int)(p[3]);
    //     p[0] = sum / 8;
    //     p[1] = sum / 8;
    //     paula_sndbufpt = (ushort*)(((byte*)paula_sndbufpt) + 4 * 2);
    // }
    #if SOUNDSTUFF > 1
    if (outputsample == 0)
        return;
    len = paula_sndbufpt - paula_sndbufpt_start;
    if (outputsample < 0)
    {
        int i;
        short* p1 = (short*)paula_sndbufpt_prev;
        short* p2 = (short*)paula_sndbufpt_start;
        for (i = 0; i < len; i++)
        {
            *p1 = (*p1 + *p2) / 2;
        }
        paula_sndbufpt = paula_sndbufpt_start;
    }
    #endif

    if ((byte*)paula_sndbufpt - (byte*)paula_sndbuffer >= paula_sndbufsize)
    {
        finish_sound_buffer();
        paula_sndbufpt = paula_sndbuffer;
    }

    #if SOUNDSTUFF > 1
    while (doublesample-- > 0)
    {
        memcpy(paula_sndbufpt, paula_sndbufpt_start, len * 2);
        paula_sndbufpt += len;
        if ((byte*)paula_sndbufpt - (byte*)paula_sndbuffer >= paula_sndbufsize)
        {
            finish_sound_buffer();
            paula_sndbufpt = paula_sndbuffer;
        }
    }
    #endif
}

static __forceinline void clear_sound_buffers()
{
    memset(paula_sndbuffer, 0, paula_sndbufsize);
    paula_sndbufpt = paula_sndbuffer;
}

#define PUT_SOUND_WORD(b) do { *(ushort*)paula_sndbufpt = b; paula_sndbufpt = (ushort*)(((byte*)paula_sndbufpt) + 2); } while (0)
#define PUT_SOUND_WORD_LEFT(b) do { if (g_curr_conf.sound_filter) b = filter(b, &sound_filter_state[0]); PUT_SOUND_WORD(b); } while (0)
#define PUT_SOUND_WORD_RIGHT(b) do { if (g_curr_conf.sound_filter) b = filter(b, &sound_filter_state[1]); PUT_SOUND_WORD(b); } while (0)
#define PUT_SOUND_WORD_LEFT2(b) do { if (g_curr_conf.sound_filter) b = filter(b, &sound_filter_state[2]); PUT_SOUND_WORD(b); } while (0)
#define PUT_SOUND_WORD_RIGHT2(b) do { if (g_curr_conf.sound_filter) b = filter(b, &sound_filter_state[3]); PUT_SOUND_WORD(b); } while (0)

#define PUT_SOUND_WORD_MONO(b) PUT_SOUND_WORD_LEFT(b)
#define SOUND16_BASE_VAL 0
// #define SOUND8_BASE_VAL 128

#define DEFAULT_SOUND_MAXB 16384
// #define DEFAULT_SOUND_MINB 16384
// #define DEFAULT_SOUND_BITS 16
// #define DEFAULT_SOUND_FREQ 44100
// #define HAVE_STEREO_SUPPORT

#define FILTER_SOUND_OFF 0
#define FILTER_SOUND_LED_EMUL 1
#define FILTER_SOUND_LED_ON 2
#define FILTER_SOUND_LED_OFF 3

#define FILTER_SOUND_TYPE_A500 0
#define FILTER_SOUND_TYPE_A1200 1

#define SOUND_INTERPOLATION_NONE 0
#define SOUND_INTERPOLATION_ANTI 1
#define SOUND_INTERPOLATION_SINC 2
#define SOUND_INTERPOLATION_RH 3
#define SOUND_INTERPOLATION_CRUX 4

struct dsaudiomodes
{
    int ch;
    DWORD ksmode;
};

namespace Sound
{
    extern const int SOUND_FREQ;
}

// extern int sounddrivermask;
// #define SOUNDDRIVER_DS 1
// #define SOUNDDRIVER_WASAPI 2
// #define SOUNDDRIVER_OPENAL 4
// #define SOUNDDRIVER_PORTAUDIO 8